﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Zcu.PanelComposer.ViewModels.Core;
using Zcu.PanelComposer.Interfaces;
using System.ComponentModel.Composition;
using Caliburn.Micro;
using Zcu.PanelComposer.ViewModels.Events;
using Zcu.PanelComposer.Models.Model;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf;
using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;
using System.Windows.Controls;
using System.ComponentModel;

namespace Zcu.PanelComposer.ViewModels
{
    /// <summary>
    /// ViewModel pro prehled projektu.
    /// </summary>
    [Export(typeof(IModule))]
    public class ProjectDetailViewModel : NavigateScreen, IHandle<ActiveProjectEvent>
    {
        #region Fields

        /// <summary>
        /// Reference na logger.
        /// </summary>
        private readonly ILog _log = LogManager.GetLog(typeof(ProjectDetailViewModel));

        /// <summary>
        /// Formatovaci retezec pro nadpis stranky.
        /// </summary>
        public const string ProjectNameFormat = "Project detail: {0}";

        /// <summary>
        /// Reference na aktivni projekt.
        /// </summary>
        private Project _SelectedProject;

        /// <summary>
        /// Reference na 3D model.
        /// </summary>
        private Model3D _Model;

        #endregion // Fields

        #region Properties

        /// <summary>
        /// Reference na aktivni projekt.
        /// </summary>
        public Project SelectedProject
        {
            get { return _SelectedProject; }
            set
            {
                if (_SelectedProject != value) _SelectedProject = value;
                NotifyOfPropertyChange(() => SelectedProject);
                NotifyOfPropertyChange(() => ProjectName);
            }
        }

        /// <summary>
        /// Getter vracejici nadpis stranky.
        /// </summary>
        public string ProjectName 
        { 
            get 
            { 
                return SelectedProject!= null ? 
                    string.Format(ProjectNameFormat, SelectedProject.DisplayName) : DisplayName;
            } 
        }

        /// <summary>
        /// Gets or sets the model.
        /// </summary>
        /// <value>The model.</value>
        public Model3D Model 
        {
            get { return _Model; }
            set
            {
                if (_Model != value)
                    _Model = value;
                NotifyOfPropertyChange(() => Model);
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance can draw panels action.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance can draw panels action; otherwise, <c>false</c>.
        /// </value>
        public bool CanDrawPanelsAction
        {
            get { return CanFillPanelsAction; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance can fill panels action.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance can fill panels action; otherwise, <c>false</c>.
        /// </value>
        public bool CanFillPanelsAction
        { 
            get 
            { 
                return !string.IsNullOrEmpty(SelectedProject.BasePolygon); 
            } 
        }

        [Import]
        public IRepository Repository { get; private set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Konstruktor ViewModelu, prednastaveni pocatecnich hodnot.
        /// Nazev stranky (titulek), nazev modulu (kvuli navigaci) a poradni v menu.
        /// </summary>
        public ProjectDetailViewModel()
        {
            /// todo do resources
            DisplayName = "Project Detail";
            ModuleName = PageNames.ProjectDetail;
            Order = 4;
            MenuVisible = false;
        }

        #endregion // Constructor

        #region Overrided methods

        /// <summary>
        /// Udalost, ktera je volana hned po vytvoreni screenu.
        /// </summary>
        public override void OnReconstruct()
        {
            base.OnReconstruct();

            // registrace zachycovani udalosti zmeny projektu.
            EventAggregator.Subscribe(this);
        }

        /// <summary>
        /// Called when activating.
        /// </summary>
        protected override void OnActivate()
        {
            base.OnActivate();

            // vygeneruj 3D model
            Generate3DModel();

            // notifikuj guard funkce
            NotifyOfPropertyChange(() => CanFillPanelsAction);
            NotifyOfPropertyChange(() => CanDrawPanelsAction);
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Akce, vyvolana stiskem tlacitka "Rename project".
        /// </summary>
        public void RenameAction()
        {
            Navigate(PageNames.NewProject, SelectedProject);
        }

        /// <summary>
        /// Akce, vyvolana stiskem tlacitka "Draw base".
        /// </summary>
        public void DrawBaseAction()
        {
            Navigate(PageNames.Base, SelectedProject);
        }


        /// <summary>
        /// Akce, vyvolana stiskem tlacitka "Fill panels".
        /// </summary>
        public void FillPanelsAction()
        {
            MessageBox.Show("Fill panels will be implemented in phase II");
        }


        /// <summary>
        /// Akce, vyvolana stiskem tlacitka "Rename project".
        /// </summary>
        public void DrawPanelsAction()
        {
            Navigate(PageNames.Panel, SelectedProject);
        }

        #endregion // Public methods

        #region Private methods

        /// <summary>
        /// Generate 3D model.
        /// </summary>
        private void Generate3DModel()
        {
            Model3D model = null;
            string path = string.Empty;
            // kontrola, zdali project obsahuje vse pro vygenerovani 3D Modelu
            if (!string.IsNullOrEmpty(SelectedProject.BasePolygon)
                && !string.IsNullOrEmpty(SelectedProject.XamlTexture)
                ) //&& SelectedProject.Drawing != null)
            {
                // path = FileHelper.GetDrawingPath(SelectedProject.Drawing);
                // vytvorime kolekci bodu
                var poly = PointCollection.Parse(SelectedProject.BasePolygon);
                // vytvorime instanci pomocne tridy generujici 3D model
                Polygon3DModel pModel = new Polygon3DModel(poly);
                // vygenerujeme model
                model = pModel.CreateModel(SelectedProject.Floors * 50, Utils.DeserializableCanvas(SelectedProject.XamlTexture));
                model.Transform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), 180));
            }

            Model = model;
        }

        private void CreateCube2()
        {

            string fullPath = Path.GetFullPath(@"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg");

            if (!File.Exists(fullPath))
            {
                return;
            }

            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.UriSource = new Uri(fullPath);
            image.EndInit();

            // var brush = new ImageBrush(image);

            var visual = new StackPanel();
            visual.Children.Add(new Button() { Content = "Ahoj svete" });
            visual.Children.Add(new Button() { Content = "jak se mas" });


            var brush = new VisualBrush(visual);
            brush.ViewportUnits = BrushMappingMode.Absolute;
            var material = new DiffuseMaterial(brush);

            var group = new Model3DGroup();

            var normal = new Vector3D(0, -1, 0); // nejprve levou
            var up = new Vector3D(0, 0, 1);

            var mesh = new MeshGeometry3D();
            var right = Vector3D.CrossProduct(normal, up);
            var origin = new Point3D(0, 0, 0);
            var n = normal;

            Point3D p1 = origin + n - up - right;
            Point3D p2 = origin + n - up + right;
            Point3D p3 = origin + n + up + right;
            Point3D p4 = origin + n + up - right;

            mesh.Positions.Add(p1);
            mesh.Positions.Add(p2);
            mesh.Positions.Add(p3);
            mesh.Positions.Add(p4);

            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);

            mesh.TextureCoordinates.Add(new Point(0.5, 1));
            mesh.TextureCoordinates.Add(new Point(0, 1));
            mesh.TextureCoordinates.Add(new Point(0, 0));
            mesh.TextureCoordinates.Add(new Point(0.5, 0));

            mesh.TriangleIndices.Add(2);
            mesh.TriangleIndices.Add(1);
            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(3);
            mesh.TriangleIndices.Add(2);

            group.Children.Add(new GeometryModel3D(mesh, material));

            normal = new Vector3D(1, 0, 0); // pak front

            mesh = new MeshGeometry3D();
            right = Vector3D.CrossProduct(normal, up);
            origin = new Point3D(0, 0, 0);
            n = normal;

            p1 = origin + n - up - right;
            p2 = origin + n - up + right;
            p3 = origin + n + up + right;
            p4 = origin + n + up - right;

            mesh.Positions.Add(p1);
            mesh.Positions.Add(p2);
            mesh.Positions.Add(p3);
            mesh.Positions.Add(p4);

            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);

            mesh.TextureCoordinates.Add(new Point(1, 1));
            mesh.TextureCoordinates.Add(new Point(0.5, 1));
            mesh.TextureCoordinates.Add(new Point(0.5, 0));
            mesh.TextureCoordinates.Add(new Point(1, 0));

            mesh.TriangleIndices.Add(2);
            mesh.TriangleIndices.Add(1);
            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(3);
            mesh.TriangleIndices.Add(2);

            group.Children.Add(new GeometryModel3D(mesh, material));

            this.Model = group;
        }

        private void CreateCube()
        {

            // Create some materials
            var front = MaterialHelper.CreateMaterial(Colors.Green);
            var left = MaterialHelper.CreateMaterial(Colors.Red);
            var back = MaterialHelper.CreateMaterial(Colors.Blue);
            var right = MaterialHelper.CreateMaterial(Colors.Yellow);
            var up = MaterialHelper.CreateMaterial(Colors.AliceBlue);
            var down = MaterialHelper.CreateMaterial(Colors.AliceBlue);

            var group = new Model3DGroup();
            group.Children.Add(this.AddCubeSide(new Vector3D(1, 0, 0), new Vector3D(0, 0, -1), front));
            group.Children.Add(this.AddCubeSide(new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), left));
            group.Children.Add(this.AddCubeSide(new Vector3D(0, 1, 0), new Vector3D(0, 0, -1), right));
            group.Children.Add(this.AddCubeSide(new Vector3D(-1, 0, 0), new Vector3D(0, 0, 1), back));
            group.Children.Add(this.AddCubeSide(new Vector3D(0, 0, 1), new Vector3D(0, -1, 0), up));
            group.Children.Add(this.AddCubeSide(new Vector3D(0, 0, -1), new Vector3D(0, 1, 0), down));

            this.Model = group;
        }

        /// <summary>
        /// The add cube side.
        /// </summary>
        /// <param name="normal">
        /// The normal.
        /// </param>
        /// <param name="up">
        /// The up.
        /// </param>
        /// <param name="fileName">
        /// The file name.
        /// </param>
        /// <returns>
        /// </returns>
        private GeometryModel3D AddCubeSide(Vector3D normal, Vector3D up, Material material)
        {
            var mesh = new MeshGeometry3D();
            var right = Vector3D.CrossProduct(normal, up);
            var origin = new Point3D(0, 0, 0);
            var n = normal;

            Point3D p1 = origin + n - up - right;
            Point3D p2 = origin + n - up + right;
            Point3D p3 = origin + n + up + right;
            Point3D p4 = origin + n + up - right;

            mesh.Positions.Add(p1);
            mesh.Positions.Add(p2);
            mesh.Positions.Add(p3);
            mesh.Positions.Add(p4);

            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);

            mesh.TextureCoordinates.Add(new Point(1, 1));
            mesh.TextureCoordinates.Add(new Point(0, 1));
            mesh.TextureCoordinates.Add(new Point(0, 0));
            mesh.TextureCoordinates.Add(new Point(1, 0));

            mesh.TriangleIndices.Add(2);
            mesh.TriangleIndices.Add(1);
            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(3);
            mesh.TriangleIndices.Add(2);

            return new GeometryModel3D(mesh, material);
        }

        #endregion

        #region Handle events

        /// <summary>
        /// Zachyceni udalosti "aktivace projektu".
        /// </summary>
        /// <param name="message">The message.</param>
        public void Handle(ActiveProjectEvent message)
        {
            SelectedProject = (Project)message.Project;
        }

        #endregion
    }
}
